#include "monitor.h"
#include "temu.h"
#include "watchpoint.h"

#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <expr.h>

void cpu_exec(uint32_t);

void display_reg();

/* We use the `readline' library to provide more flexibility to read from stdin. */
char *rl_gets()
{
	static char *line_read = NULL;

	if (line_read)
	{
		free(line_read);
		line_read = NULL;
	}

	line_read = readline("(temu) ");

	if (line_read && *line_read)
	{
		add_history(line_read);
	}

	return line_read;
}

static int cmd_help(char *args);
static int cmd_c(char *args);
static int cmd_q(char *args);
static int cmd_si(char *args);
static int cmd_info(char *args);
static int cmd_x(char *args);
static int cmd_p(char *args);
static int cmd_w(char *args);
static int cmd_d(char *args);

static struct
{
	char *name;
	char *description;
	int (*handler)(char *);
} cmd_table[] = {
	{"help", "Display informations about all supported commands", cmd_help},
	{"c", "Continue the execution of the program", cmd_c},
	{"q", "Exit NEMU", cmd_q},
	/* TODO: Add more commands */
	{"si", "Run N single steps", cmd_si},
	{"info", "Print regs' or watchpoints' state", cmd_info},
	{"x", "Scan the memory", cmd_x},
	{"p", "Evaluate a expression", cmd_p},
	{"w", "Set a watchpoint", cmd_w},
	{"d", "Delete a watchpoint", cmd_d}

};

#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

static int cmd_help(char *args)
{
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if (arg == NULL)
	{
		/* no argument given */
		for (i = 0; i < NR_CMD; i++)
		{
			printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else
	{
		for (i = 0; i < NR_CMD; i++)
		{
			if (strcmp(arg, cmd_table[i].name) == 0)
			{
				printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		printf("help's Unknown command '%s'\n", arg);
	}
	return 0;
}

static int cmd_c(char *args)
{
	cpu_exec(-1);
	return 0;
}

static int cmd_q(char *args)
{
	return -1;
}

static int cmd_si(char *args)
{
	if (args == NULL)
	{
		// defalut to 1
		cpu_exec(1);
	}
	else
	{
		int step = atoi(args);
		if (step < 1)
		{
			fprintf(stderr, "执行步数应当大于或等于1.\n");
			return -1;
		}
		cpu_exec(step);
	}
	return 0;
}

static int cmd_info(char *args)
{
	char *subcmd[] = {"r", "w"};
	// int i;
	char *arg = strtok(NULL, " ");
	if (arg != NULL)
	{
		if (strcmp(arg, subcmd[0]) == 0) //"r"打印寄存器状态
		{
			display_reg();
			return 0;
		}
		else if (strcmp(arg, subcmd[1]) == 0) //'w'打印监视点信息
		{
			display_watchpoint();
			return 0;
		}
		else
		{
			printf("Unknown command '%s'\n", arg);
			return 0;
		}
	}
	printf("Lack of parameter!\n");
	return 0;
}

static int cmd_x(char *args)
{
	char *arg = strtok(NULL, " ");
	if (arg == NULL)
	{
		printf("Lack of parameter!\n");
		return 0;
	}
	int printNumber = atoi(arg);
	if (printNumber == 0)
	{
		printf("Unknown command '%s'\n", arg);
		return 0; // N=0时无法输出
	}
	// printf("printNumber is %d\n",printNumber);//测试能否输出N

	// char* e = strtok(NULL, " ") ;
	// char* expression = e;
	// printf("expression is %s\n", expression);
	// while( e != NULL ) {
	// 	printf( "e is %s\n", e );

	// 	e = strtok(NULL, " ");
	//     strcat( expression , e);
	// 	printf("expression is %s\n", expression);

	//  }
	char *expression = strtok(NULL, " ");
	if (expression == NULL)
	{
		printf("Lack of parameter!\n");
		return 0;
	}
	printf("expression is %s\n", expression); //测试能否输出expr

	bool *success = false;
	// char *str;
	uint32_t addr = expr(expression, success);
	int i;
	for (i = 0; i < printNumber; i++)
	{
		printf("0x%08x:\t0x%08x\n", addr, mem_read(addr, 4));
		addr = addr + 4;
	}
	return 0;
}

static int cmd_p(char *args)
{
	bool *success = false;
	if (args == NULL)
	{
		printf("Lack of parameter!\n");
		return 0;
	}
	uint32_t computedResult = expr(args, success);
	printf("0x%08x\n", computedResult);
	return 0;
}

// static int cmd_w(char *args)
// {
// 	if (args == NULL)
// 	{
// 		printf("Lack of parameter!\n");
// 		return 0;
// 	}
// 	bool *success = false;
// 	WP *newWatchpoint = new_watchpoint();
// 	strcpy(newWatchpoint->expression, args);
// 	newWatchpoint->address = expr(args, success);
// 	newWatchpoint->value = mem_read(newWatchpoint->address, 4);
// 	printf("Set watchpoint NO.%d on 0x%08x\n", newWatchpoint->NO, newWatchpoint->address);
// 	return 0;
// }

static int cmd_w(char *args)
{
	if (args == NULL)
	{
		printf("Lack of parameter!\n");
		return 0;
	}
	WP *f;
	f = new_wp();
	bool success;
	printf("Watchpoint %d\t%s\n", f->NO, args);
	f->val = expr(args, &success);
	strcpy(f->expr, args);
	printf("Watchpoint value %d\t\n", f->val);
	return 0;
}

// static int cmd_d(char *args)
// {
// 	if (args == NULL)
// 	{
// 		printf("Lack of parameter!\n");
// 		return 0;
// 	}
// 	int watchpointNO = atoi(args);
// 	free_watchpoint(watchpointNO);
// 	return 0;
// }
static int cmd_d(char *args)
{
	int num;
	sscanf(args, "%d", &num);
	printf("Delete watchpoint %d\n", num);
	delete_wp(num);
	return 0;
}

void ui_mainloop()
{
	while (1)
	{
		char *str = rl_gets();
		char *str_end = str + strlen(str);

		/* extract the first token as the command */
		char *cmd = strtok(str, " ");
		if (cmd == NULL)
		{
			continue;
		}

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		char *args = cmd + strlen(cmd) + 1;
		if (args >= str_end)
		{
			args = NULL;
		}

		int i;
		for (i = 0; i < NR_CMD; i++)
		{
			if (strcmp(cmd, cmd_table[i].name) == 0)
			{
				if (cmd_table[i].handler(args) < 0)
				{
					return;
				}
				break;
			}
		}

		if (i == NR_CMD)
		{
			printf("UI's Unknown command '%s'\n", cmd);
		}
	}
}